/* Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "mbcs.h"

int ConvertStringEncoding( char *from_encoding , char *to_encoding , char *str , size_t str_len , char **encoding_buf , size_t *encoding_buf_size , size_t *encoding_buf_len )
{
	iconv_t		ic ;
	char		*in_ptr = NULL ;
	size_t		in_left_len ;
	char		*out_ptr = NULL ;
	size_t		out_left_len ;
	char		*out_base = NULL ;
	size_t		out_size ;
	char		*new_out_base = NULL ;
	size_t		new_out_size ;
	size_t		ret = 0 ;
	size_t		out_len ;
	
	ic = iconv_open( to_encoding , from_encoding ) ;
	if( ic == (iconv_t)-1 )
		return MBCS_ERROR_INVALID_ENCODING;
	
	in_ptr = str ;
	in_left_len = str_len ;
	if( (*encoding_buf) )
	{
		out_ptr = (*encoding_buf) ;
		out_size = (*encoding_buf_size) ;
		out_left_len = out_size - 1 ;
	}
	else
	{
		out_left_len = str_len * 3 ;
		out_size = out_left_len + 1 ;
		out_base = (char*)malloc( out_size ) ;
		if( out_base == NULL )
		{
			iconv_close( ic );
			return MBCS_ERROR_ALLOC;
		}
		out_ptr = out_base ;
	}
	while( in_left_len > 0 )
	{
		ret = iconv( ic , & in_ptr , & in_left_len , & out_ptr , & out_left_len ) ;
		if( ret == (size_t)-1 )
		{
			switch( errno )
			{
				case E2BIG:
					if( (*encoding_buf) )
						return MBCS_ERROR_INPUT_BUFFER_TOO_SMALL;
					new_out_size = out_left_len*2 + 1 ;
					new_out_base = (char*)realloc( out_base , new_out_size ) ;
					if( new_out_base == NULL )
					{
						iconv_close( ic );
						free( out_base );
						return MBCS_ERROR_ALLOC;
					}
					out_base = new_out_base ;
					out_size = new_out_size ;
					out_ptr = (out_ptr-out_base) + out_base ;
					out_left_len += out_left_len ;
					break;
				case EILSEQ:
				case EINVAL:
					iconv_close( ic );
					if( encoding_buf == NULL )
						free( out_base );
					return MBCS_ERROR_INVALID_INPUT;
				default :
					iconv_close( ic );
					if( encoding_buf == NULL )
						free( out_base );
					return MBCS_ERROR_UNKNOW;
			}
		}
	}
	
	iconv_close( ic );
	
	out_len = out_size-1-out_left_len ;
	out_base[out_len] = '\0' ;
	if( (*encoding_buf) == NULL )
	{
		(*encoding_buf) = out_base ;
		if( encoding_buf_size )
			(*encoding_buf_size) = out_size ;
		if( encoding_buf_len )
			(*encoding_buf_len) = out_len ;
	}
	else
	{
		if( encoding_buf_len )
			(*encoding_buf_len) = out_len ;
	}
	return 0;
}

